#define INTRO
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Windows.Media.Animation;
using System.Runtime.InteropServices;
using System.Windows.Interop;
using System.ComponentModel;
using Microsoft.Win32;
using ViewOnCore.Helper.WPF;
using ViewOn.UI;
using ViewOn.UI.Control;
using ViewOn.UI.Dialog;
using ViewOn.UI.VisualModeContainer;
using ViewOn.Kernel;
using ViewOn.AddIns;
using ViewOn.AddInsHostContract;
using ViewOn.AddInsHostContract.Media;
using ViewOn.Helper.Memory;
using ViewOn.Properties;
using ViewOnCore;
using ViewOnCore.HostContract;
using ViewOnCore.Helper.Debug;
using ViewOnCore.Visual;

namespace ViewOn
{

    #region enum : TvWindowDisplayedPage

    public enum TvWindowDisplayedPage
    {
        None,
        Intro,
        Player,
        OrderCredit
    }

    #endregion

    /// <summary>
    /// Interaction logic for TvWindow.xaml
    /// </summary>
    public partial class TvWindow : System.Windows.Window
    {

        #region Variables

        //---- Notify icon
        private System.Windows.Forms.NotifyIcon notifyIcon;

        //---- View mode switch
        private Dictionary<VisualPartMode, VisualPartDisplayInformation> _visualPartModeInformations = new Dictionary<VisualPartMode, VisualPartDisplayInformation>();

        private MiniViewModeContainer _miniViewModeContainer;
        private NormalViewModeContainer _normalViewModeContainer;

        #endregion

        #region Constructor

        public TvWindow()
        {
            InitializeComponent();

            //---- Initialize the application
            InitializeApplication();

            _miniViewModeContainer = new MiniViewModeContainer();
            _normalViewModeContainer = new NormalViewModeContainer();
            _normalViewModeContainer.HorizontalAlignment = HorizontalAlignment.Stretch;
            _normalViewModeContainer.VerticalAlignment = VerticalAlignment.Stretch;
            this.Content = _normalViewModeContainer;

            StartApplication();

            //---- Add ins
            AddInStore.AddInRegistered += new AddInEvent(AddInsRepository_AddInRegistered);
            AddInStore.AddInUnregistered += new AddInEvent(AddInsRepository_AddInUnregistered);
            Host.VisualService.VisualPartModeChanged += new VisualPartModeChangedEvent(VisualService_VisualPartModeChanged);

            //---- Tray
            // Configure and show a notification icon in the system tray
            this.notifyIcon = new System.Windows.Forms.NotifyIcon();
            //this.notifyIcon.BalloonTipText = "";
            this.notifyIcon.Text = "";
            this.notifyIcon.Icon = new System.Drawing.Icon(AppDomain.CurrentDomain.BaseDirectory + "\\ViewOn.ico");
            this.notifyIcon.Visible = true;
            //this.notifyIcon.ShowBalloonTip(10000);
            this.notifyIcon.DoubleClick += new EventHandler(notifyIcon_DoubleClick);

            this.LocationChanged += new EventHandler(TvWindow_LocationChanged);
            this.Loaded += new RoutedEventHandler(TvWindow_Loaded);
        }

        void TvWindow_Loaded(object sender, RoutedEventArgs e)
        {
            //---- Set the default visual pack
            if (Host.UIService.ApplyVisualPack(Host.UIService.LoadVisualPack(Host.UIService.CurrentVisualPackName)) != null)
            {
                Host.UIService.CurrentVisualPackName = "Default";
                Host.UIService.ApplyVisualPack(Host.UIService.LoadVisualPack(Host.UIService.CurrentVisualPackName));
            }

            //---- Restore window state
            this.WindowState = WindowState.Maximized;

            if (MediaKernel.MustCheckForAssociations)
            {
                QuestionDialog dialog = new QuestionDialog(QuestionDialog.QuestionDialogType.YesNo,
                                                        "ViewOn.tv is not the default player for media files",
                                                        "Do you want to change this ?",
                                                        null,
                                                        Globalization.AskEachTimeOnStartup,
                                                        350);

                dialog.ShowDialog(SkinWindow.MainWindow.Desktop,
                                                        new ChildWindowDialogClosedHandler(OnConfirmSetAssociations));
            }
        }

        #endregion

        #region InitializeApplication

        private void InitializeApplication()
        {
            //---- Initialize the application
            ViewOnHostContract.InstallHost();

            //---- Set the application window
            Host.ApplicationWindow = this;

            //---- Prepare the registration of the 'libraries' used in the application
            Host.InsureSerialRegistration(typeof(Un4seen.Bass.BassNet), typeof(SharePodLib.SharePodLib));

            int windowHandle = new System.Windows.Interop.WindowInteropHelper(this).Handle.ToInt32();
            BassPlayer.Initialize((int)windowHandle);

        }

        private void StartApplication()
        {
            ViewOn.AddIns.AddInStore.Rebuild(Environment.CurrentDirectory + "\\AddIns\\");

            MediaKernel.Initialize();

            //---- Auto update
            if (Host.AutoUpdateService.CheckUpdateAtStartup)
                Host.AutoUpdateService.CheckVersion();

            //---- Improve memory management
            MemoryHelper.Start();

            if (App.StartupArgs.Length > 0)
            {
                string path = "";
                for (int index = 0; index < App.StartupArgs.Length; index++)
                {
                    if (index > 0)
                        path += " ";
                    path += App.StartupArgs[index];
                }

                MediaKernel.InitializeCommandLineFile(path);
            }

            //MediaKernel.InitializeCommandLineFile("D:\\Films\\Death.Race.FRENCH.R5.LD.XViD-TGK.By.Agecanonix.avi");
            //MediaKernel.InitializeCommandLineFile("D:\\temp\\MP3\\tomas barford - saturdaya.mp3");

            //MediaKernel.InitializeCommandLineFile("http://sce.m2ktalk.com:8020/listen.pls");
            //MediaKernel.InitializeCommandLineFile("http://www.di.fm/mp3/electro.pls");

            //---- Hide the splash screen
            App.SplashScreen.Close(TimeSpan.Zero);
        }

        #endregion

        #region OnConfirmSetAssociations

        private void OnConfirmSetAssociations(ChildWindowDecorator window)
        {
            //---- Check if it is Ok to delete
            QuestionDialog dialog = window.Parent as QuestionDialog;

            Host.SettingsService.CheckAssociationsAtStartup = (bool)dialog.IsOptionChecked;

            if (!dialog.IsOkYes)
                return;

            foreach (string ext in Host.MediaService.SupportedMusicExtensions.Keys)
                Host.MediaService.SetMediaFileAssociation(ext);
            foreach (string ext in Host.MediaService.SupportedVideoExtensions.Keys)
                Host.MediaService.SetMediaFileAssociation(ext);
            foreach (string ext in Host.MediaService.SupportedRadioExtensions.Keys)
                Host.MediaService.SetMediaFileAssociation(ext);
        }

        #endregion

        #region Tray Icon

        private WindowState _previousState = WindowState.Normal;
        private WindowState _newState = WindowState.Normal;

        void notifyIcon_DoubleClick(object sender, EventArgs e)
        {
            //WindowInteropHelper helper = new WindowInteropHelper(this);
            //BringWindowToTop((int)helper.Handle);

            //IntPtr hwnd = new WindowInteropHelper(this).Handle;
            //if (hwnd != IntPtr.Zero)
            //int r = BringWindowToTop(hwnd.ToInt32());

            if (Host.VisualService.VisualPartMode == VisualPartMode.Mini)
                return;

            WindowState = _previousState;

            //this.Show();

            if (WindowState == WindowState.Normal || WindowState == WindowState.Maximized)
            {
                this.Topmost = true;
                this.Topmost = false;
            }
        }

        protected override void OnStateChanged(EventArgs e)
        {
            _previousState = _newState;
            _newState = WindowState;

            base.OnStateChanged(e);

            //if (WindowState == WindowState.Minimized)
            //    this.Hide();
        }

        #endregion

        #region Windows Hook

        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);

            //---- Hook the window
            HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource;
            if (hwndSource != null)
                hwndSource.AddHook(new HwndSourceHook(this.hwndSourceHook));
        }

        // Win32 constants
        private const int DBT_DEVTYP_DEVICEINTERFACE = 5;
        private const int DBT_DEVTYP_HANDLE = 6;
        private const int BROADCAST_QUERY_DENY = 0x424D5144;
        private const int WM_DEVICECHANGE = 0x0219;
        private const int DBT_DEVICEARRIVAL = 0x8000; // system detected a new device
        private const int DBT_DEVICEQUERYREMOVE = 0x8001;   // Preparing to remove (any program can disable the removal)
        private const int DBT_DEVICEREMOVECOMPLETE = 0x8004; // removed 
        private const int DBT_DEVTYP_VOLUME = 0x00000002; // drive type is logical volume

        [StructLayout(LayoutKind.Sequential)]
        public struct DEV_BROADCAST_VOLUME
        {
            public int dbcv_size;
            public int dbcv_devicetype;
            public int dbcv_reserved;
            public int dbcv_unitmask;
        }

        IntPtr hwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            if (msg == WM_DEVICECHANGE)
            {
                string drive = null;
                if ((int)wParam == DBT_DEVICEARRIVAL || (int)wParam == DBT_DEVICEREMOVECOMPLETE)
                {
                    int devType = Marshal.ReadInt32(lParam, 4);
                    if (devType == DBT_DEVTYP_VOLUME)
                    {
                        DEV_BROADCAST_VOLUME vol = (DEV_BROADCAST_VOLUME)Marshal.PtrToStructure(lParam, typeof(DEV_BROADCAST_VOLUME));
                        drive = FirstDriveFromMask(vol.dbcv_unitmask).ToString();
                    }
                }

                if ((int)wParam == DBT_DEVICEARRIVAL && drive != null)
                    ((MediaService)Host.MediaService).OnHardwareDeviceConnected(drive);

                else if ((int)wParam == DBT_DEVICEREMOVECOMPLETE && drive != null)
                    ((MediaService)Host.MediaService).OnHardwareDeviceDisconnected(drive);
            }

            return IntPtr.Zero;
        }

        // Finds the first valid drive letter from a mask of drive letters. The
        // mask must be in the format bit 0 = A, bit 1 = B, bit 3 = C, etc.
        // A valid drive letter is defined when the corresponding bit is set to
        // 1.
        // Returns the drive letter that was first found.
        private char FirstDriveFromMask(int unitmask)
        {
            int i;

            for (i = 0; i < 26; ++i)
            {
                if ((unitmask & 0x1) > 0)
                    break;
                unitmask = unitmask >> 1;
            }

            return (char)(i + (int)'A');
        }

        #endregion

        #region LoadLocation/SaveLocation

        private VisualPartDisplayInformation LoadLocation()
        {
            try
            {
                VisualPartDisplayInformation information = new VisualPartDisplayInformation();

                RegistryKey key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\VMS\\ViewOnTv\\" + Host.VisualService.VisualPartMode.ToString() + "\\WindowInformation", true);
                if (key == null)
                    return null;

                information.Left = Double.Parse((string)key.GetValue("Left"));
                information.Top = Double.Parse((string)key.GetValue("Top"));
                information.Width = Double.Parse((string)key.GetValue("Width"));
                information.Height = Double.Parse((string)key.GetValue("Height"));

                if (key.GetValue("WindowState").Equals(WindowState.Normal.ToString()))
                    information.WindowState = WindowState.Normal;
                else if (key.GetValue("WindowState").Equals(WindowState.Minimized.ToString()))
                    information.WindowState = WindowState.Minimized;
                else if (key.GetValue("WindowState").Equals(WindowState.Maximized.ToString()))
                    information.WindowState = WindowState.Maximized;

                return information;
            }
            catch
            {
                return null;
            }
        }

        private void SaveLocation(VisualPartMode mode)
        {
            string keyName = "SOFTWARE\\VMS\\ViewOnTv\\" + mode.ToString() + "\\WindowInformation";
            RegistryKey key = Registry.CurrentUser.OpenSubKey(keyName, true);
            if (key == null)
                key = Registry.CurrentUser.CreateSubKey(keyName);

            if (mode == VisualPartMode.Mini && this.ActualHeight > 20)
                return;

            key.SetValue("Left", this.Left);
            key.SetValue("Top", this.Top);
            key.SetValue("Width", this.ActualWidth);
            key.SetValue("Height", this.ActualHeight);
            key.SetValue("WindowState", this.WindowState);
        }

        #endregion

        #region Magnetize

        private bool _magneticMoving = false;
        private int MAGNETIC_GAP = 8;

        void TvWindow_LocationChanged(object sender, EventArgs e)
        {
            Magnetic();
        }

        private void Magnetic()
        {
            if (Host.VisualService.VisualPartMode != VisualPartMode.Mini || _magneticMoving || WindowState != WindowState.Normal)
                return;

            bool onLeft = false;
            bool onRight = false;
            bool onTop = false;
            bool onBottom = false;

            Rect workingArea = System.Windows.SystemParameters.WorkArea;

            int workingAreaWidth = (int)System.Windows.SystemParameters.VirtualScreenWidth;
            int workingAreaHeight = (int)workingArea.Height;
            int newX = (int)(this.Left - workingArea.X);
            int newY = (int)(this.Top - workingArea.Y);

            if (newX > workingArea.Width)
                workingAreaHeight = (int)System.Windows.SystemParameters.VirtualScreenHeight;

            // Check
            if (newX < MAGNETIC_GAP) onLeft = true;
            if (newY < MAGNETIC_GAP) onTop = true;
            if (newX + Width > workingAreaWidth - MAGNETIC_GAP) onRight = true;
            if (newY + Height > workingAreaHeight - MAGNETIC_GAP) onBottom = true;

            // Move
            if (onLeft) newX = 0;
            if (onRight) newX = (int)(workingAreaWidth - Width);
            if (onTop) newY = 0;
            if (onBottom) newY = (int)(workingAreaHeight - Height);

            if (onLeft || onRight || onTop || onBottom)
            {
                _magneticMoving = true;

                // Move the mouse
                System.Windows.Forms.Cursor.Position = new System.Drawing.Point(System.Windows.Forms.Cursor.Position.X + (newX - ((int)(this.Left - workingArea.X))),
                                                                                System.Windows.Forms.Cursor.Position.Y + (newY - ((int)(this.Top - workingArea.Y))));

                this.Left = workingArea.X + newX;
                this.Top = workingArea.Y + newY;

                _magneticMoving = false;
            }

            //---- Save the new location
            SaveLocation(Host.VisualService.VisualPartMode);
        }

        #endregion

        #region Add-ins management

        void AddInsRepository_AddInRegistered(object addIn)
        {
        }

        void AddInsRepository_AddInUnregistered(object addIn)
        {
        }

        #endregion

        #region Events : Visual-service

        void VisualService_VisualPartModeChanged(VisualPartMode previousMode, VisualPartMode currentMode)
        {
            Dispatcher.Invoke(DispatcherPriority.Send, (DelegateInvoker)delegate { AsyncVisualPartModeChanged(previousMode, currentMode); });
        }

        private void AsyncVisualPartModeChanged(VisualPartMode previousMode, VisualPartMode currentMode)
        {
            this.Visibility = Visibility.Hidden;

            VisualPart visualPart = null;

            //---- Save the previous window information
            VisualPartDisplayInformation information = new VisualPartDisplayInformation();
            information.WindowState = this.WindowState;
            information.Left = this.Left;
            information.Top = this.Top;
            information.Width = this.Width;
            information.Height = this.Height;
            if (previousMode != VisualPartMode.NotDefined)
            {
                _visualPartModeInformations.Remove(previousMode);
                _visualPartModeInformations.Add(previousMode, information);
            }

            //---- Save the windows location
            SaveLocation(previousMode);

            //---- Set the right View container
            IVisualModeContainer container = null;
            if (currentMode == VisualPartMode.Mini)
                container = _miniViewModeContainer;
            else if (currentMode == VisualPartMode.Normal)
                container = _normalViewModeContainer;

            if (this.Content != container)
            {
                this.Content = container;
                ((FrameworkElement)container).HorizontalAlignment = HorizontalAlignment.Stretch;
                ((FrameworkElement)container).VerticalAlignment = VerticalAlignment.Stretch;
            }

            //---- Choose the Add In view to display
            foreach (VisualPart part in Host.VisualService.VisualParts)
                if (part.Mode == Host.VisualService.VisualPartMode &&
                    VisualPartHelper.IsCurrentMediaDeviceSupported(part.MediaTypeTags))
                {
                    visualPart = part;
                    break;
                }

            if (visualPart == null)
                return;

            try
            {
                container.ChangeVisualPart(visualPart);
            }
            catch (Exception exception)
            {
                LogManager.Handle(exception);
            }

            //---- Set up the window location and size
            VisualPartDisplayInformation leavedInformation;
            if (_visualPartModeInformations.ContainsKey(currentMode))
                leavedInformation = _visualPartModeInformations[currentMode];
            else
                leavedInformation = null;

            //---- Load the position and size information for this mode
            VisualPartDisplayInformation displayInformation = LoadLocation();

            //---- Request initial display information for this mode
            if (displayInformation == null)
            {
                // Request initial information
                displayInformation = visualPart.OnRequestDisplayInformation(information, leavedInformation);
            }

            //---- Update the window position
            this.LocationChanged -= new EventHandler(TvWindow_LocationChanged);

            this.SizeToContent = SizeToContent.Manual;

            // Special case
            if (displayInformation.Left != Double.MinValue)
                this.Left = displayInformation.Left;
            if (displayInformation.Top != Double.MinValue)
                this.Top = displayInformation.Top;
            if (displayInformation.Width != Double.MinValue)
                this.Width = displayInformation.Width;
            if (displayInformation.Height != Double.MinValue)
                this.Height = displayInformation.Height;

            this.WindowState = displayInformation.WindowState;

            if (displayInformation.Left != Double.MinValue)
                this.Left = displayInformation.Left;
            if (displayInformation.Top != Double.MinValue)
                this.Top = displayInformation.Top;
            if (displayInformation.Width != Double.MinValue)
                this.Width = displayInformation.Width;
            if (displayInformation.Height != Double.MinValue)
                this.Height = displayInformation.Height;

            // Insure that the view use 100% of the space
            ((Control)container).Width = this.Width;
            ((Control)container).Height = this.Height;

            this.LocationChanged += new EventHandler(TvWindow_LocationChanged);

            //---- Display the window
            if (this.Visibility != Visibility.Visible)
                this.Visibility = Visibility.Visible;
        }

        #endregion

    }

}